//
// XCODE-IU9011 Device Driver for Windows 7
// Copyright (C)2012 Jhlim
//

#pragma warning(disable:4200)  
#pragma warning(disable:4201)  
#pragma warning(disable:4214)  

#include <initguid.h>
#include <wdm.h>
#include "usbdi.h"
#include "usbdlib.h"

#pragma warning(default:4200)
#pragma warning(default:4201)
#pragma warning(default:4214)

#include <wdf.h>
#include <wdfusb.h>

#include "ringBuf.h"
#include "public.h"
#include "prototypes.h"
#include "ioctl.h"
#include "readPipe.h"

EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd;
EVT_WDF_DEVICE_PREPARE_HARDWARE XIU9011_EvtDevicePrepareHardware;
EVT_WDF_DEVICE_RELEASE_HARDWARE XIU9011_EvtDeviceReleaseHardware;
EVT_WDF_DEVICE_SURPRISE_REMOVAL XIU9011_EvtDeviceSurpriseRemoval;
EVT_WDF_DEVICE_D0_ENTRY XIU9011_EvtDeviceD0Entry;
EVT_WDF_DEVICE_D0_EXIT XIU9011_EvtDeviceD0Exit;
EVT_WDF_DEVICE_FILE_CREATE XIU9011_EvtDeviceFileCreate;
EVT_WDF_FILE_CLOSE XIU9011_EvtFileClose;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL XIU9011_EvtIoDeviceControl; 
EVT_WDF_IO_QUEUE_IO_READ XIU9011_EvtIoRead; 
EVT_WDF_IO_QUEUE_IO_WRITE XIU9011_EvtIoWrite;

DRIVER_INITIALIZE DriverEntry;

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT  DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

{
    WDF_DRIVER_CONFIG       config;
    NTSTATUS                ntStatus;

    KdPrint(("[XIU9011] DriverEntry %s - %s\n", __DATE__, __TIME__));
   
    // WDF_DRIVER_CONFIG initialize
    WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd);

    // WDFDRIVER obj initialize
    ntStatus = WdfDriverCreate(
        DriverObject,
        RegistryPath,
        WDF_NO_OBJECT_ATTRIBUTES, 
        &config,          
        WDF_NO_HANDLE);
        
    if (!NT_SUCCESS(ntStatus)) 
    {
        KdPrint(("[XIU9011] DriverEntry: WdfDriverCreate Err 0x%x \n",ntStatus));
    }

    return ntStatus;
}

NTSTATUS EvtDeviceAdd(
    IN WDFDRIVER       Driver,
    IN PWDFDEVICE_INIT  DeviceInit
    ) 
{
    WDFDEVICE   device;
    NTSTATUS    ntStatus;

    WDF_OBJECT_ATTRIBUTES attributes;

    WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;

    WDF_FILEOBJECT_CONFIG   fileConfig;
    
    WDF_IO_QUEUE_CONFIG     ioQueueConfig;
    WDFQUEUE  queue;
        
    UNREFERENCED_PARAMETER(Driver);

    // register callback function for PNP and Power management
    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
    pnpPowerCallbacks.EvtDevicePrepareHardware = XIU9011_EvtDevicePrepareHardware;
    pnpPowerCallbacks.EvtDeviceReleaseHardware = XIU9011_EvtDeviceReleaseHardware;
    pnpPowerCallbacks.EvtDeviceSurpriseRemoval = XIU9011_EvtDeviceSurpriseRemoval;
    pnpPowerCallbacks.EvtDeviceD0Entry = XIU9011_EvtDeviceD0Entry;
    pnpPowerCallbacks.EvtDeviceD0Exit = XIU9011_EvtDeviceD0Exit;
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
    
    // register callback function of create & close
    WDF_FILEOBJECT_CONFIG_INIT(
        &fileConfig,
        XIU9011_EvtDeviceFileCreate, 
        XIU9011_EvtFileClose,
        WDF_NO_EVENT_CALLBACK
        );
    WdfDeviceInitSetFileObjectConfig(DeviceInit,&fileConfig, WDF_NO_OBJECT_ATTRIBUTES);
        
    // create WDFDEVICE obj including DEVICE_CONTEXT
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
	WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT);
	ntStatus = WdfDeviceCreate(&DeviceInit, &attributes, &device);
    if (!NT_SUCCESS(ntStatus)) 
    {
        KdPrint(("[XIU9011] EvtDeviceAdd: WdfDeviceCreate Err 0x%x \n",ntStatus));
        goto EXIT;    
    }

    // register GUID
    ntStatus = WdfDeviceCreateDeviceInterface(device,(LPGUID) &GUID_XIU9011Drv, NULL);
    if (!NT_SUCCESS(ntStatus)) 
    {
        KdPrint(("[XIU9011] EvtDeviceAdd: WdfDeviceCreateDeviceInterface err 0x%x \n",ntStatus));
        goto EXIT;
    }

    // initialize WDFQUEUE
    WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioQueueConfig, WdfIoQueueDispatchSequential);
    ioQueueConfig.EvtIoDeviceControl  = XIU9011_EvtIoDeviceControl;
    ioQueueConfig.EvtIoRead = XIU9011_EvtIoRead; 
    ioQueueConfig.EvtIoWrite = XIU9011_EvtIoWrite;
    ntStatus = WdfIoQueueCreate(device,
                              &ioQueueConfig,
                              WDF_NO_OBJECT_ATTRIBUTES,
                              &queue);// pointer to default queue
    if (!NT_SUCCESS(ntStatus)) 
    {
        KdPrint(("[XIU9011] EvtDeviceAdd: WdfIoQueueCreate err 0x%x \n",ntStatus));
        goto EXIT;
    }

    // ring buffer create
    ntStatus = rb_init(device);
    if (!NT_SUCCESS(ntStatus))
    {
        KdPrint(("[XIU9011] EvtDeviceAdd: rb_init err 0x%x \n",ntStatus));
        goto EXIT;
    }
    
EXIT:
    
    return ntStatus;
}

NTSTATUS XIU9011_EvtDeviceD0Entry(
    IN WDFDEVICE  device,
    IN WDF_POWER_DEVICE_STATE  previousState
    )
{
    PDEVICE_CONTEXT  pDeviceContext;
    NTSTATUS  ntStatus;
   
#if DBG
    KdPrint(("[XIU9011] XIU9011_EvtDeviceD0Entry PreviousState %d \n",previousState));
#else
    UNREFERENCED_PARAMETER(previousState);
#endif

    pDeviceContext = GetDeviceContext(device);

    ntStatus = WdfIoTargetStart(WdfUsbTargetPipeGetIoTarget(pDeviceContext->BulkReadPipe));
    if (!NT_SUCCESS(ntStatus))
    {
        KdPrint(("[XIU9011] XIU9011_EvtDeviceD0Entry : WdfIoTargetStart Err \n"));
	}
	
    return ntStatus;
}

NTSTATUS XIU9011_EvtDeviceD0Exit(
    IN WDFDEVICE  device,
    IN WDF_POWER_DEVICE_STATE  targetState
    )
{
    PDEVICE_CONTEXT  p_deviceContext;
    NTSTATUS  ntStatus;
    
    p_deviceContext = GetDeviceContext(device); 
    
#if DBG
    KdPrint(("[XIU9011] XIU9011_EvtDeviceD0Exit TargetState %d \n",targetState));
#else
    UNREFERENCED_PARAMETER(targetState);
#endif

   WdfIoTargetStop(WdfUsbTargetPipeGetIoTarget(p_deviceContext->BulkReadPipe), WdfIoTargetCancelSentIo);
                    
	return STATUS_SUCCESS;
}

NTSTATUS XIU9011_EvtDevicePrepareHardware(
    IN WDFDEVICE Device,
    IN WDFCMRESLIST ResourceList,
    IN WDFCMRESLIST ResourceListTranslated
    )
{
    NTSTATUS ntStatus;
    PDEVICE_CONTEXT deviceContext;
	WDF_USB_DEVICE_SELECT_CONFIG_PARAMS configParams;
	WDF_USB_CONTINUOUS_READER_CONFIG  contReaderConfig;

    UNREFERENCED_PARAMETER(ResourceList);
    UNREFERENCED_PARAMETER(ResourceListTranslated);
    
    KdPrint(("[XIU9011] XIU9011_EvtDevicePrepareHardware \n"));
    
 	deviceContext = GetDeviceContext(Device); 
	
	ntStatus = WdfUsbTargetDeviceCreate(Device, WDF_NO_OBJECT_ATTRIBUTES, &deviceContext->WdfUsbTargetDevice);
    if (!NT_SUCCESS(ntStatus))
    {
        KdPrint(("[XIU9011] XIU9011_EvtDevicePrepareHardware : WdfUsbTargetDeviceCreate Err \n"));
		return ntStatus;
	}
	
	// get configuration_description
	ntStatus = XIU9011_GetConfiguratonDescriptor(Device, deviceContext);
    if (!NT_SUCCESS(ntStatus))
    {
        KdPrint(("[XIU9011] XIU9011_EvtDevicePrepareHardware : XIU9011_GetConfiguratonDescriptor Err \n"));
        return ntStatus;
	}

	// initialize usb device
    WDF_USB_DEVICE_SELECT_CONFIG_PARAMS_INIT_SINGLE_INTERFACE(&configParams);
    ntStatus = WdfUsbTargetDeviceSelectConfig(
    			deviceContext->WdfUsbTargetDevice,
                WDF_NO_OBJECT_ATTRIBUTES,
                &configParams);
                
    if (!NT_SUCCESS(ntStatus))
    { 
        KdPrint(("[XIU9011] XIU9011_EvtDevicePrepareHardware : WdfUsbTargetDeviceSelectConfig Err \n"));
	    return ntStatus;
    }

	// Initialize pipe
	XIU9011_InitializePipeInformation(
		Device,
		deviceContext,
		configParams.Types.SingleInterface.ConfiguredUsbInterface,
		configParams.Types.SingleInterface.NumberConfiguredPipes);
		
	// Initialize continuous reading operation
    WDF_USB_CONTINUOUS_READER_CONFIG_INIT(
        &contReaderConfig,
        EvtUsbTargetPipeReadComplete,
        Device,
        64);
        
    ntStatus = WdfUsbTargetPipeConfigContinuousReader(
                                      deviceContext->BulkReadPipe,
                                      &contReaderConfig
                                      );
    if (!NT_SUCCESS(ntStatus))
    { 
        KdPrint(("[XIU9011] XIU9011_EvtDevicePrepareHardware : WdfUsbTargetPipeConfigContinuousReader Err (%x) \n", ntStatus));
	    return ntStatus;
    }

	
	return STATUS_SUCCESS;
}

NTSTATUS XIU9011_EvtDeviceReleaseHardware(
    IN WDFDEVICE device,
    IN WDFCMRESLIST resourceListTranslated
    )
{
    UNREFERENCED_PARAMETER(device);
    UNREFERENCED_PARAMETER(resourceListTranslated);

	KdPrint(("[XIU9011] XIU9011_EvtDeviceReleaseHardware \n"));

	return STATUS_SUCCESS;
}


VOID XIU9011_EvtDeviceSurpriseRemoval(
    IN WDFDEVICE  Device
    )
{
	UNREFERENCED_PARAMETER(Device);

	KdPrint(("[XIU9011] XIU9011_EvtDeviceSurpriseRemoval \n"));
}

VOID XIU9011_EvtDeviceFileCreate(
    IN WDFDEVICE            Device,
    IN WDFREQUEST           Request,
    IN WDFFILEOBJECT        FileObject
    )
{
    KdPrint(("[XIU9011] XIU9011_EvtDeviceFileCreate\n"));
    
    // Notify Completion
    WdfRequestComplete(Request, STATUS_SUCCESS);
}

VOID XIU9011_EvtFileClose(
    IN WDFFILEOBJECT  FileObject
    )
{
    KdPrint(("[XIU9011] XIU9011_EvtFileClose\n"));
}

VOID XIU9011_EvtIoRead(
    IN WDFQUEUE         Queue,
    IN WDFREQUEST       Request,
    IN size_t           Length
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PMDL Mdl;
    ULONG_PTR completeLength = 0;
        
    // Notify Completion
    WdfRequestCompleteWithInformation(Request,ntStatus,completeLength);
}

VOID XIU9011_EvtIoWrite(
    IN WDFQUEUE         Queue,
    IN WDFREQUEST       Request,
    IN size_t           Length
    )
{
	PDEVICE_CONTEXT deviceContext;
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PMDL Mdl;
    ULONG_PTR completeLength = 0;
    
    // Notify Completion
    WdfRequestCompleteWithInformation(Request,ntStatus,completeLength);
}

VOID XIU9011_EvtIoDeviceControl(
    IN WDFQUEUE   queue,
    IN WDFREQUEST request,
    IN size_t     outputBufferLength,
    IN size_t     inputBufferLength,
    IN ULONG      ioControlCode
    )
{
	WDFDEVICE           device;
    PDEVICE_CONTEXT     p_deviceContext;
	NTSTATUS			ntStatus;
	ULONG       		completeLength;
	BOOLEAN             requestPending;
	 
	ntStatus = STATUS_SUCCESS;
	completeLength = 0;
	
	device = WdfIoQueueGetDevice(queue);
    p_deviceContext = GetDeviceContext(device);
	
	switch(ioControlCode) 
	{
		case IOCTL_XIU9011_WRITE:
		    //KdPrint(("[XIU9011] IOCTL_XIU9011_WRITE\n"));
		    ntStatus= XIU9011_WritePipeHandler(p_deviceContext->BulkWritePipe, request, (ULONG_PTR)&completeLength);		    
			break;
				
		case IOCTL_XIU9011_READ:
   		    //KdPrint(("[XIU9011] IOCTL_XIU9011_READ\n"));
		    ntStatus = XIU9011_ReadDataHander(request, &(p_deviceContext->rb), (ULONG_PTR)&completeLength);
			break;
				
		case IOCTL_XIU9011_READ_CNT:
   		    //KdPrint(("[XIU9011] IOCTL_XIU9011_READ_CNT\n"));
		    ntStatus = XIU9011_ReadCntHandler(request, &(p_deviceContext->rb), (ULONG_PTR)&completeLength);
			break;
				
		case IOCTL_XIU9011_CANCEL:
   		    //KdPrint(("[XIU9011] IOCTL_XIU9011_CANCEL\n"));
			ntStatus = XIU9011_CanceltHandler(p_deviceContext->WdfUsbTargetDevice, (ULONG_PTR)&completeLength);
			break;
				
		case IOCTL_XIU9011_SOFTRESET:
   		    //KdPrint(("[XIU9011] IOCTL_XIU9011_SOFTRESET\n"));
			ntStatus = XIU9011_SoftResetHandler(p_deviceContext->WdfUsbTargetDevice, (ULONG_PTR)&completeLength);
			break;
				
		case IOCTL_XIU9011_ABORT:
   		    //KdPrint(("[XIU9011] IOCTL_XIU9011_ABORT\n"));
			ntStatus = XIU9011_AbortHandler(p_deviceContext->WdfUsbTargetDevice, (ULONG_PTR)&completeLength);
			break;
					
		default:
		    //KdPrint(("[XIU9011] STATUS_INVALID_DEVICE_REQUEST\n"));
       		ntStatus = STATUS_INVALID_DEVICE_REQUEST;
       		break;
	}
	
    WdfRequestCompleteWithInformation(request,ntStatus,completeLength); 
}
